// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CommonHelper.cs" company="">
//   
// </copyright>
// <summary>
//   The common helper.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace Skymate.Utilities
{
    using System;
    using System.ComponentModel;
    using System.Configuration;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Security.Cryptography;
    using System.Web.Hosting;

    using Skymate.Extensions;

    /// <summary>
    ///     The common helper.
    /// </summary>
    public static class CommonHelper
    {
        /// <summary>
        ///     Gets a value indicating whether is dev environment.
        /// </summary>
        public static bool IsDevEnvironment
        {
            get
            {
                if (!HostingEnvironment.IsHosted)
                {
                    return true;
                }

                if (HostingEnvironment.IsDevelopmentEnvironment)
                {
                    return true;
                }

                if (Debugger.IsAttached)
                {
                    return true;
                }

                // if there's a 'SmartStore.NET.sln' in one of the parent folders,
                // then we're likely in a dev environment
                if (FindSolutionRoot(HostingEnvironment.MapPath("~/")) != null)
                {
                    return true;
                }

                return false;
            }
        }

        /// <summary>
        /// Generate random digit code
        /// </summary>
        /// <param name="length">
        /// Length
        /// </param>
        /// <returns>
        /// Result string
        /// </returns>
        public static string GenerateRandomDigitCode(int length)
        {
            var random = new Random();
            var str = string.Empty;
            for (var i = 0; i < length; i++)
            {
                str = string.Concat(str, random.Next(10).ToString());
            }

            return str;
        }

        /// <summary>
        /// Returns an random interger number within a specified rage
        /// </summary>
        /// <param name="min">
        /// Minimum number
        /// </param>
        /// <param name="max">
        /// Maximum number
        /// </param>
        /// <returns>
        /// Result
        /// </returns>
        public static int GenerateRandomInteger(int min = 0, int max = 2147483647)
        {
            var randomNumberBuffer = new byte[10];
            new RNGCryptoServiceProvider().GetBytes(randomNumberBuffer);
            return new Random(BitConverter.ToInt32(randomNumberBuffer, 0)).Next(min, max);
        }

        /// <summary>
        /// Maps a virtual path to a physical disk path.
        /// </summary>
        /// <param name="path">
        /// The path to map. E.g. "~/bin"
        /// </param>
        /// <param name="findAppRoot">
        /// Specifies if the app root should be resolved when mapped directory does not exist
        /// </param>
        /// <returns>
        /// The physical path. E.g. "c:\inetpub\wwwroot\bin"
        /// </returns>
        /// <remarks>
        /// This method is able to resolve the web application root
        ///     even when it's called during design-time (e.g. from EF design-time tools).
        /// </remarks>
        public static string MapPath(string path, bool findAppRoot = true)
        {
            Guard.ArgumentNotNull(() => path);

            if (HostingEnvironment.IsHosted)
            {
                // hosted
                return HostingEnvironment.MapPath(path);
            }

            // not hosted. For example, running in unit tests or EF tooling
            var baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
            path = path.Replace("~/", string.Empty).TrimStart('/').Replace('/', '\\');

            var testPath = Path.Combine(baseDirectory, path);

            if (findAppRoot /* && !Directory.Exists(testPath)*/)
            {
                // most likely we're in unit tests or design-mode (EF migration scaffolding)...
                // find solution root directory first
                var dir = FindSolutionRoot(baseDirectory);

                // concat the web root
                if (dir != null)
                {
                    baseDirectory = Path.Combine(dir.FullName, "Presentation\\SmartStore.Web");
                    testPath = Path.Combine(baseDirectory, path);
                }
            }

            return testPath;
        }

        /// <summary>
        /// The find solution root.
        /// </summary>
        /// <param name="currentDir">
        /// The current dir.
        /// </param>
        /// <returns>
        /// The <see cref="DirectoryInfo"/>.
        /// </returns>
        private static DirectoryInfo FindSolutionRoot(string currentDir)
        {
            var dir = Directory.GetParent(currentDir);
            while (true)
            {
                if (dir == null || IsSolutionRoot(dir))
                {
                    break;
                }

                dir = dir.Parent;
            }

            return dir;
        }

        /// <summary>
        /// The is solution root.
        /// </summary>
        /// <param name="dir">
        /// The dir.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        private static bool IsSolutionRoot(DirectoryInfo dir)
        {
            return File.Exists(Path.Combine(dir.FullName, "SmartStoreNET.sln"));
        }

        /// <summary>
        /// The try convert.
        /// </summary>
        /// <param name="value">
        /// The value.
        /// </param>
        /// <param name="convertedValue">
        /// The converted value.
        /// </param>
        /// <typeparam name="T">
        /// </typeparam>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool TryConvert<T>(object value, out T convertedValue)
        {
            return TryConvert(value, CultureInfo.InvariantCulture, out convertedValue);
        }

        /// <summary>
        /// The try convert.
        /// </summary>
        /// <param name="value">
        /// The value.
        /// </param>
        /// <param name="culture">
        /// The culture.
        /// </param>
        /// <param name="convertedValue">
        /// The converted value.
        /// </param>
        /// <typeparam name="T">
        /// </typeparam>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool TryConvert<T>(object value, CultureInfo culture, out T convertedValue)
        {
            return Misc.TryAction(delegate { return value.Convert<T>(culture); }, out convertedValue);
        }

        /// <summary>
        /// The try convert.
        /// </summary>
        /// <param name="value">
        /// The value.
        /// </param>
        /// <param name="to">
        /// The to.
        /// </param>
        /// <param name="convertedValue">
        /// The converted value.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool TryConvert(object value, Type to, out object convertedValue)
        {
            return TryConvert(value, to, CultureInfo.InvariantCulture, out convertedValue);
        }

        /// <summary>
        /// The try convert.
        /// </summary>
        /// <param name="value">
        /// The value.
        /// </param>
        /// <param name="to">
        /// The to.
        /// </param>
        /// <param name="culture">
        /// The culture.
        /// </param>
        /// <param name="convertedValue">
        /// The converted value.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public static bool TryConvert(object value, Type to, CultureInfo culture, out object convertedValue)
        {
            return Misc.TryAction(delegate { return value.Convert(to, culture); }, out convertedValue);
        }

        /*

		public static ExpandoObject ToExpando(object value)
		{
			Guard.ArgumentNotNull(() => value);

			var anonymousDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(value);
			IDictionary<string, object> expando = new ExpandoObject();
			foreach (var item in anonymousDictionary)
			{
				expando.Add(item);
			}
			return (ExpandoObject)expando;
		}
*/

        /// <summary>
        /// The get type converter.
        /// </summary>
        /// <param name="type">
        /// The type.
        /// </param>
        /// <returns>
        /// The <see cref="TypeConverter"/>.
        /// </returns>
        public static TypeConverter GetTypeConverter(Type type)
        {
            return ConversionExtensions.GetTypeConverter(type);
        }

        /// <summary>
        /// Gets a setting from the application's <c>web.config</c> <c>appSettings</c> node
        /// </summary>
        /// <typeparam name="T">
        /// The type to convert the setting value to
        /// </typeparam>
        /// <param name="key">
        /// The key of the setting
        /// </param>
        /// <param name="defValue">
        /// The default value to return if the setting does not exist
        /// </param>
        /// <returns>
        /// The casted setting value
        /// </returns>
        public static T GetAppSetting<T>(string key, T defValue = default(T))
        {
            Guard.ArgumentNotEmpty(() => key);

            var setting = ConfigurationManager.AppSettings[key];

            if (setting == null)
            {
                return defValue;
            }

            return setting.Convert<T>();
        }
    }
}